Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Commit 1f6ff4bf9980c77864cbd03cb3da7d529efcec00


Parents : f5e9450
Author : Mark Qvist <mark@unsigned.io>
Date : 2025-11-26T13:23:00+01:00

Added FilePlayer primitive

Changes

3 files changed, 75 insertions(+), 1 deletions(-)


Diff

diff --git a/LXST/Primitives/Players.py b/LXST/Primitives/Players.py
new file mode 100644
index 0000000..08b3547
--- /dev/null
+++ b/LXST/Primitives/Players.py
@@ -0,0 +1,52 @@
+import LXST
+import time
+import os
+
+from LXST.Sinks import LineSink
+from LXST.Sources import OpusFileSource
+
+class FilePlayer():
+ def __init__(self, path=None, device=None, loop=False):
+ self._file_path = path
+ self._playback_device = None
+ self.__loop = loop
+ self.__source = None
+ self.__sink = LineSink(self._playback_device)
+ self.__raw = LXST.Codecs.Raw()
+ self.__loopback = LXST.Sources.Loopback()
+ self.__output_pipeline = LXST.Pipeline(source=self.__loopback, codec=self.__raw, sink=self.__sink)
+ self.__input_pipeline = None
+ if path: self.set_source(self._file_path)
+
+ @property
+ def running(self):
+ if not self.__source: return False
+ else: return self.__source.should_run
+
+ @property
+ def playing(self): return self.running
+
+ def set_source(self, path=None):
+ if not path: return
+ else:
+ if not os.path.isfile(path): raise OSError(f"File not found: {path}")
+ else:
+ self.__source = OpusFileSource(path, loop=self.__loop)
+ self.__input_pipeline = LXST.Pipeline(source=self.__source, codec=self.__raw, sink=self.__loopback)
+
+ def loop(self, loop=True):
+ if loop == True: self.__loop = True
+ else: self.__loop = False
+ if self.__source: self.__source.loop = self.__loop
+
+ def start(self):
+ if self.__source:
+ self.__input_pipeline.start()
+ self.__output_pipeline.start()
+
+ def stop(self):
+ if self.__source:
+ self.__input_pipeline.stop()
+ self.__output_pipeline.stop()
+
+ def play(self): self.start()
\ No newline at end of file

diff --git a/LXST/Sources.py b/LXST/Sources.py
index 4da546b..49cfa38 100644
--- a/LXST/Sources.py
+++ b/LXST/Sources.py
@@ -240,7 +240,7 @@ class LineSource(LocalSource):
class OpusFileSource(LocalSource):
MAX_FRAMES = 128
- DEFAULT_FRAME_MS = 70
+ DEFAULT_FRAME_MS = 100
TYPE_MAP_FACTOR = np.iinfo("int16").max
def __init__(self, file_path, target_frame_ms=DEFAULT_FRAME_MS, loop=False, codec=None, sink=None, timed=False):

diff --git a/examples/fileplayer.py b/examples/fileplayer.py
new file mode 100644
index 0000000..a248dc8
--- /dev/null
+++ b/examples/fileplayer.py
@@ -0,0 +1,22 @@
+import sys
+import time
+import select
+from LXST.Primitives.Players import FilePlayer
+
+loop = False
+
+if loop:
+ player = FilePlayer("./docs/speech_stereo.opus", loop=True)
+ player.start()
+
+ while player.running:
+ i, o, e = select.select([sys.stdin], [], [], 1.0)
+ if (i): player.stop()
+
+else:
+ player = FilePlayer("./docs/speech_stereo.opus")
+ player.start()
+
+ while player.running: time.sleep(0.1)
+
+print("Playback finished")
\ No newline at end of file


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────